#include <stdio.h>
#include <openssl/bn.h>

/** 根据P和Q计算生成元
 * @param P 安全大素数
 * @param Q (P-1)/2
 * @param one 值为1的大数结构
 * @param two 值为2的大数结构
 * @return 计算所得的生成元
 */
//获取生成元g
BIGNUM * get_g(BIGNUM *P,BIGNUM *Q,const BIGNUM *one,const BIGNUM *two)
{
    BN_CTX *ctx=BN_CTX_new();
    BIGNUM *g=BN_new(); //生成元g
    BN_set_word(g,2);   //设置初始值为2
    // 1.a^2 mod P !=1
    // 2.a^Q mod P !=1 
    BIGNUM *condi1=BN_new();    //条件1
    BN_mod_exp(condi1,g,two,P,ctx);   //计算条件1
    BIGNUM *condi2=BN_new();    //条件2
    BN_mod_exp(condi2,g,Q,P,ctx);   //计算条件2  
    while(BN_is_one(condi1)||BN_is_one(condi2)) //如果有一个条件不满足
    {
        BN_add(g,g,one);    //加1
        BN_mod_exp(condi1,g,two,P,ctx);   //计算条件1    
        BN_mod_exp(condi2,g,Q,P,ctx);   //计算条件2
    }
    BN_CTX_free(ctx);
    BN_free(condi1);
    BN_free(condi2);
    return g;
}
//主函数
int main(int argc,char ** argv)
{
    BIGNUM *P=BN_new();
    BIGNUM *Q=BN_new();
    BIGNUM *rem=BN_new();
    BN_CTX *ctx=BN_CTX_new();
    BN_generate_prime(P,128,1,NULL,NULL,NULL,NULL);   //生成一个安全素数P
    BIGNUM *two=BN_new();   //2
    BIGNUM *one=BN_new();   //1
    BN_set_word(two,2); //设置为2
    BN_one(one);    //设置为1
    BN_sub(Q,P,one);    //P-1
    BN_div(Q,rem,Q,two,ctx);//计算出Q 
    BIGNUM *g=get_g(P,Q,one,two);   //计算生成元g
    printf("P g\n");
    printf("%s\n",BN_bn2hex(P));    //打印出数字对应的16进制
    printf("%s\n",BN_bn2hex(g));       
    BN_free(P);     //释放资源
    BN_free(Q);
    BN_free(two);
    BN_free(one);   
    BN_free(rem);   
    BN_free(g);   
    BN_CTX_free(ctx);
    return 0;
}
